#!/bin/bash
#
# A bit of hackery to update everything that is humanly possible
# that maybe related to an older version of python. This script can
# be run as many times as you like.
#
# OLD_PY_VER      = old python version we are upgrading from
# NEW_PY_VER      = new python version we are upgrading to
# PKGS_EXCEPTIONS = packages that should NOT be re-emerged for any reason
# PKGS_MANUAL     = packages that should be re-emerged even if they don't
#                   fit the criteria (eg. ones that have python compiled
#                   statically)
#
# Runtime Variables:
#
# PKGS_TO_REMERGE = list of packages we deem to need re-emerging
# PKGS_OK         = list of packages that should be merged without any problems
# PKGS_MISSING    = list of packages that are installed, but cannot be merged
#                   because they have been pruned from portage
# PKGS_MASKED     = list of packages that are installed, but masked.
#

NEW_PY_VER=$(python -V 2>&1 | sed 's:Python ::' | cut -d. -f1-2)

PKGS_EXCEPTIONS="dev-lang/python sys-apps/portage"
PKGS_MANUAL="app-office/gnumeric app-office/dia dev-libs/boost x11-libs/vte"

# portage variables
PKG_DBDIR=/var/db/pkg
PORTDIR=`portageq portdir`
PORTDIR_OVERLAYS=`portageq portdir_overlay`

PRETEND=0
DIRECT_ONLY=0
IGNORE_VERSIONS=0
PKGS_TO_REMERGE=""
PKGS_COUNT_REMERGE=0
PORTAGE_PYTHON="/usr/bin/python"

SUPPORTED_PMS="portage pkgcore paludis"
PMS_COMMAND=( "emerge" "pmerge" "paludis" )
PMS_OPTIONS=( "-vD1" "-Do" "-i1" )

# load the gentoo-style info macros, but hack to get around
# it thinking this is an rc script
EBUILD="1"
source /etc/init.d/functions.sh

usage() {
	echo "usage: python-updater [-h|-p|-o X.X]"
	echo " --help, -h		help"
	echo " --pretend, -p		pretend (don't do anything)"
	echo " --old-version, -o  X.X	set old python version to upgrade from [default: ${OLD_PY_VER}]"
	echo " --ignore-versions, -i	ignore versions when remerging packages (still respects SLOTs)"
	echo " --direct-only		don't consider indirect aka manually added packages"
	echo " --package-manager, -P name	select between ${SUPPORTED_PMS} [default: portage]"
}

#
# Sanity check
#

if [ -z "${PORTDIR}" ]; then
	eerror "Unable to proceed. Can not find PORTDIR. Make sure the command:"
	eerror " "
	eerror "  portageq portdir"
	eerror " "
	eerror "returns a value. If it doesn't, make sure you have updated to"
	eerror "latest portage version."
	eerror " "
	eerror "Report bugs to http://bugs.gentoo.org/"
	exit 1
fi	

#
#
# Command Line Parsing
#
#
while [ -n "$1" ]; do
	case "$1" in
		-h|--help)
			usage
			exit 0
			;;
		-p|--pretend)
			PRETEND=1
			;;
		-o|--old-version)
			shift
			OLD_PY_VER="$1"
			;;
		-i|--ignore-versions)
			IGNORE_VERSIONS=1
			;;
		--direct-only)
			DIRECT_ONLY=1
			;;
		-P|--package-manager)
			shift
			PACKAGE_MANAGER="$1"
			case "${PACKAGE_MANAGER}" in
			    portage|pkgcore|paludis)
			        ;;
			    *)
			        echo "unrecognised package manager selected. please select between ${SUPPORTED_PMS}"
			        exit
			        ;;
			esac

			# PMS_INDEX is used to select the right commands and options for the selected package manager
			PMS_INDEX=0
			for PM in ${SUPPORTED_PMS}; do
			    [[ ${PM} == ${PACKAGE_MANAGER} ]] && break
			    PMS_INDEX=$((${PMS_INDEX} + 1))
			done
			;;
		*)
			usage
			echo "unrecognised option: $1"
			exit 0
			;;
	esac
	shift
done

# Determine old python version
if [[ -z "${OLD_PY_VER}" ]]; then
	for old in 2.5 2.4 2.3 2.2 2.1; do
		if [ "${old}" != "${NEW_PY_VER}" ]; then
			if [ -e /usr/bin/python${old} ] ; then
				OLD_PY_VER=${old}
				break;
			fi
		fi
	done
fi


if [ -z "${OLD_PY_VER}" ] ; then
    eerror "Can't determine any previous Python version(s)."
    exit 1
fi


#
# Test where portage is, in python2.2 or somewhere else?
#
for py in /usr/bin/python /usr/bin/python${OLD_PY_VER} /usr/bin/python${NEW_PY_VER}; do
	if ${py} -c "import portage" > /dev/null 2>&1; then
		PORTAGE_PYTHON=${py}
		break;
	fi
done

#
#
# Find all packages that have installed something in
# /usr/lib/python${OLD_PY_VER}
#
#
OLD_MODULES_DIRS="/usr/lib/python${OLD_PY_VER} /usr/lib32/python${OLD_PY_VER} /usr/lib64/python${OLD_PY_VER}"
OLD_INCLUDE_DIR=/usr/include/python${OLD_PY_VER}

einfo "Starting Python Updater from ${OLD_PY_VER} to ${NEW_PY_VER} :"
einfo "Searching for packages with files in ${OLD_MODULES_DIRS} .."

# iterate thru all the installed package's contents
for content in `find ${PKG_DBDIR} -name CONTENTS`; do
    # extract the category, package name and package version
    CATPKGVER=$(echo ${content} | sed "s:${PKG_DBDIR}/\(.*\)/CONTENTS:\1:")

    # exclude packages that are an exception, like portage and python itself.
    exception=0
    for exp in ${PKGS_EXCEPTIONS}; do
    	if [ -n "$(echo ${CATPKGVER} | grep ${exp})" ]; then
			exception=1
			break;
		fi
    done
	
    if [ ${exception} = 1 ]; then
       continue;
    fi

    # Check if package is in PKGS_MANUAL
    if [[ DIRECT_ONLY -ne 1 ]]; then
    	for pkg in ${PKGS_MANUAL}; do
		if [ -n "$(echo ${CATPKGVER} | grep ${pkg})" ]; then
			exception=2
			break;
		fi
    	done
    fi

    SLOT=$(< ${content/CONTENTS/SLOT})

    # replace version number by SLOT if IGNORE_VERSIONS != 0
    if [[ IGNORE_VERSIONS -ne 0 ]]; then
	CATPKGVER="${CATPKGVER%%-[0-9]*}:${SLOT}"
    else
    	CATPKGVER="=${CATPKGVER}"
    fi

    if [ ${exception} = 2 ]; then
	PKGS_TO_REMERGE="${PKGS_TO_REMERGE} ${CATPKGVER}"
	einfo "Adding to list (manually): ${CATPKGVER}"
	continue;
    fi

    for OLD_MODULES_DIR in ${OLD_MODULES_DIRS}; do
        if fgrep "${OLD_MODULES_DIR}" ${content} > /dev/null; then
	    PKGS_TO_REMERGE="${PKGS_TO_REMERGE} ${CATPKGVER}"
            einfo "Adding to list: ${CATPKGVER}"
        elif fgrep "${OLD_INCLUDE_DIR}" ${content} > /dev/null; then
	    PKGS_TO_REMERGE="${PKGS_TO_REMERGE} ${CATPKGVER}"
        fi
    done
done

# only pretending?
[[ PRETEND -eq 1 ]] && PMS_OPTIONS[${PMS_INDEX}]="${PMS_OPTIONS[${PMS_INDEX}]}p"

# (Pretend to) remerge packages
if [[ -n "${PKGS_TO_REMERGE}" ]]; then
	${PMS_COMMAND[${PMS_INDEX}]} ${PMS_OPTIONS[${PMS_INDEX}]} ${PKGS_TO_REMERGE}
else
	einfo "No packages needs to be remerged."
fi
